//
//  MazingPrinter.swift
//  MZPrinter
//
//  Created by lin tendy on 15/11/5.
//  Copyright © 2015年 Mazing Inc. All rights reserved.
//

import Foundation
import UIKit

class MazingPrinter {
    
    // 字符集，用 CFStringEncodings.GB_2312_80 会报错
    let encoding = CFStringConvertEncodingToNSStringEncoding(UInt32(CFStringEncodings.GB_18030_2000.rawValue))
    
    //
    // 打印机每行支持的最大字符长度（最小字体，以英文字符衡量）
    // 子类应该 override
    func getMaxTextLength() -> Int {
        // 默认是 80mm 纸宽的
        return 40
    }
    
    // 返回头部的横线，两倍粗体，不同纸张不一样
    // 子类应该 override
    func getHeaderHorizonLine() -> String {
        return ""
    }
    
    // 返回一个横线，默认字体，不同纸张不一样
    // 子类应该 override
    func getHorizonLine() -> String {
        return ""
    }
    
    // 打印地址，不同的纸张打印地址的字体可能不同
    // 子类应该 override
    func printAddress(address: String) {
        // do nothing
    }
    
    //
    // 产生 num 个空格
    // 为了兼容不同的打印机，使用中文的空格代替英文空格
    // 因为英文字符在不同的打印机的宽度不同
    //
    func makeSpace(num: Int) -> String {
        var s = ""
        let len = num / 2
        for var i=0; i < len; ++i {
            s = s + "　"
        }
        if (num % 2) == 1 {
            s = s + "　"
        }
        return s
    }
    
    // 打印一个菜的具体内容，包括名称、规格、数量、价格
    // 动态计算每一行的文字宽度，使文字始终匹配纸张宽度
    // 当要打印规格（content）时，如果文本超过纸张的宽度，规格在下一行打印
    func printItem(item: Dictionary<String, AnyObject>) {
        // 名称
        let name = item["name"] as! String
        // 规格 2015/11/5 19:49 去掉打印规格，以后可能会加深，保留这些注释
        //let content = item["content"] as! String
        // 数量
        let number = item["number"] as! Int
        let totalFee = item["totalFee"] as! Int
        let isDeliveryFee = item["isDeliveryFee"] as! Bool
        
        // 组装要打印的文本
        var tempLine = name
        let MAX_LEN = getMaxTextLength()
        if isDeliveryFee {
            var centText = "免收"
            if totalFee > 0 {
                centText = formatCent(totalFee)
            }
            // 空格的长度 = 可以打印的总长度 - “配送费”的长度（6） - totalFee格式化后的长度
            let spaceLen = MAX_LEN - ConvertUtil.getTextLength(centText) - ConvertUtil.getTextLength(name)
            if spaceLen + 1 > 0 {
                // 配送费这里修正 1 个空格的宽度，不知为什么会少1个 -_-
                tempLine = name + makeSpace(spaceLen + 1) + centText
            }
            else {
                tempLine = name + " " + centText
            }
            
//            BLEPrinter.runCmd(data: NSString(string: tempLine).dataUsingEncoding(encoding)!)
//            BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
 
        }
        else {
            
            if number > 0 {
                // 加上数量
                tempLine = tempLine + "Ｘ" + String(number)
            }
            
            //
            // 保留下面关于 content 的注释
            //
            
            //if !content.isEmpty {
            //    // 加上规格
            //    tempLine = tempLine + "(" + content + ")"
            //}
            // 格式化价格
            let centText = formatCent(totalFee)
            
            // 拼接要打印的内容
            tempLine = tempLine + " " + centText
            let len = ConvertUtil.getTextLength(tempLine)
            
            // 判断是否超长
            if len > MAX_LEN {
                // 规格放在下一行
                // 重写拼接除了规格以外的内容
                tempLine = name
                if number > 0 {
                    tempLine = tempLine + "×" + String(number)
                }
                let spaceLen = MAX_LEN - ConvertUtil.getTextLength(centText) - ConvertUtil.getTextLength(tempLine)
                
                // 在美食名称和价格之间加上空格
                if spaceLen > 0 {
                    tempLine = tempLine + makeSpace(spaceLen) + centText
                }
                else {
                    tempLine = tempLine + " " + centText
                }
                
//                BLEPrinter.runCmd(data: NSString(string: tempLine).dataUsingEncoding(encoding)!)
//                BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
                //if !content.isEmpty {
                //    BLEPrinter.runCmd(data: NSString(string: "(" + content + ")").dataUsingEncoding(encoding)!)
                //    BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
                //}

            }
            else {
                // 如果 len != MAX_LEN，增加相应的空格
                if (len < MAX_LEN) {
                    // 重新拼打印的内容
                    tempLine = name
                    if number > 0{
                        tempLine = tempLine + "×" + String(number)
                    }
                    //if !content.isEmpty {
                    //    tempLine = tempLine + "(" + content + ")"
                    //}
                    // 计算空格
                    let spaceLen = MAX_LEN - ConvertUtil.getTextLength(centText) - ConvertUtil.getTextLength(tempLine)
                    if spaceLen > 0 {
                        tempLine = tempLine + makeSpace(spaceLen) + centText
                    }
                    else {
                        tempLine = name + " " + centText
                    }
                }
                
//                BLEPrinter.runCmd(data: NSString(string: tempLine).dataUsingEncoding(encoding)!)
//                BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
            }
        }
        
        
    }
    
    // 初始化打印机
    func initPrinter() {
        BLEPrinter.runCmd(data: PrinterUtil.turnOnPrinter())
        BLEPrinter.runCmd(data: PrinterUtil.chosePrintMode(0x00))
        BLEPrinter.runCmd(data: PrinterUtil.initPrinter());
    
    }
    
    // 打印Mazing标题
    // logo: logo图片路径
    func printMazingHeader(logo: String) {
        let bytes = PrinterUtil.decodeBitmap(logo)
        if bytes.count > 0 {
            BLEPrinter.runCmd(data: PrinterUtil.alignLeft());
            BLEPrinter.runCmd(data: bytes)
 
        }
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(2));
        BLEPrinter.runCmd(data: NSString(string: getHeaderHorizonLine()).dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(0));
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
    }
    
    // ==========
    // 打印测试标题
    // ==========
    func printTestHeader() {
        
        BLEPrinter.runCmd(data: PrinterUtil.alignCenter())
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(2));
        BLEPrinter.runCmd(data: NSString(string: "测试打印").dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(0));
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        BLEPrinter.runCmd(data:PrinterUtil.printAndFeedLines(UInt8( 1 )))
  
    }
    
    // 打印餐厅名，不考虑超长的情况
    // name: 餐厅名
    // brief: 简介
    // servicePhone: 客服电话
    func printStoreName(name: String, brief: String, servicePhone: String) {
        BLEPrinter.runCmd(data: PrinterUtil.printLineHeight(70))
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(1))
        BLEPrinter.runCmd(data: PrinterUtil.alignCenter())
        BLEPrinter.runCmd(data: NSString(string: name).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data:PrinterUtil.fontSizeSetBig(0))
        if !brief.isEmpty {
            BLEPrinter.runCmd(data:PrinterUtil.printAndFeedLines(UInt8(1)))
            BLEPrinter.runCmd(data: NSString(string: brief).dataUsingEncoding(encoding)!)
        }
        //BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        if !servicePhone.isEmpty {
            BLEPrinter.runCmd(data:PrinterUtil.printAndFeedLines(UInt8(1)))
            BLEPrinter.runCmd(data: NSString(string: servicePhone).dataUsingEncoding(encoding)!)
        }
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        BLEPrinter.runCmd(data: PrinterUtil.printLineNormalHeight())
        BLEPrinter.runCmd(data:PrinterUtil.printAndFeedLines(UInt8( 1 )))
    }
    
    // 打印订单号，不考虑超长的情况
    // orderNo: 订单号
    func printOrderNumber(orderNo: String) {
        let txt = "订单号：" + orderNo
        BLEPrinter.runCmd(data:PrinterUtil.alignLeft());
        BLEPrinter.runCmd(data: NSString(string: txt).dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
    }
    
    // 打印时间，不考虑超长的情况
    // label: 标题
    // time: 要打印的时间，精确到分钟
    func printTime(label: String, time: String) {
        BLEPrinter.runCmd(data:PrinterUtil.alignLeft());
        BLEPrinter.runCmd(data: NSString(string: label + time).dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
    }
    
    // 打印订单计数号码：比如 #99
    // serialNumber: 比如：#99
    // paied: 是否已付款
    func printSerialNumber(serialNumber: String, paied: Bool) {
        var txt = ""
        if (paied) {
            txt = serialNumber + "（已付款)"
        }
        else {
            txt = serialNumber + "（未付款）"
        }
        BLEPrinter.runCmd(data:PrinterUtil.alignLeft());
        BLEPrinter.runCmd(data: NSString(string: txt).dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
    }
    
    // 打印联系方式，不考虑超长的情况
    // phone: 电话
    // name:  联系人姓名
    // remark: 留言
    func printContact(phone: String, name: String, remark: String) {
        BLEPrinter.runCmd(data:PrinterUtil.alignLeft());
        
        BLEPrinter.runCmd(data: NSString(string: phone + "（" + name + "）").dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        var txt = ""
        if remark.isEmpty {
            txt = "留言：无"
        }
        else {
            txt = "留言：" + remark
        }
        BLEPrinter.runCmd(data: NSString(string: txt).dataUsingEncoding(encoding)!);
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
        

    }
    
    //
    // 打印订单详情的美食列表
    // details: 美食列表
    func printOrderDetails(details: [AnyObject]) {
        BLEPrinter.runCmd(data: NSString(string: getHorizonLine()).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data: PrinterUtil.printLineHeight(70))
        for item in details {
            printItem(item as! Dictionary<String, AnyObject>)
        }
        BLEPrinter.runCmd(data: PrinterUtil.printLineNormalHeight())
        BLEPrinter.runCmd(data: NSString(string: getHorizonLine()).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        

    }
    
    //
    // 打印合计，不考虑超长的情况
    // label: 标题，比如“合计：”
    // price: 价钱，单位：分
    // paid:  是否已付款
    func printTotalPrice(label: String, price: Int, paied: Bool) {
        var txt = formatCent(price)
        BLEPrinter.runCmd(data:PrinterUtil.emphasizedOn());
        if paied {
            txt = "（已付款）" + label + txt
            BLEPrinter.runCmd(data: PrinterUtil.alignRight())
            BLEPrinter.runCmd(data: NSString(string: txt).dataUsingEncoding(encoding)!)
        }
        else {
            
            txt = label + txt
            BLEPrinter.runCmd(data:PrinterUtil.alignLeft());
            let spaceLen = getMaxTextLength() - ConvertUtil.getTextLength("（未付款）") - ConvertUtil.getTextLength(txt) - 1  // 减少1个才能对齐，不知道为什么
            if spaceLen > 0 {
                BLEPrinter.runCmd(data: NSString(string: makeSpace(spaceLen)).dataUsingEncoding(encoding)!)
            }
            BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(1));
            BLEPrinter.runCmd(data: NSString(string: "未付款").dataUsingEncoding(encoding)!)
            BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(0));
            BLEPrinter.runCmd(data: NSString(string: txt).dataUsingEncoding(encoding)!)
            
        }
        BLEPrinter.runCmd(data:PrinterUtil.emphasizedOff());
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
    }
    
    // 
    // 格式化分 为 元
    //
    private func formatCent(cent: Int) -> String {
        let floatValue = Float(cent) / 100.0
        let txt = "￥" + String(format: "%.1f", floatValue)
        return txt
    }
    
    //
    // 打印 footer 并切纸
    // qrcodeImg: 二维码图片
    func printMazingFooter(qrcodeImg: String) {
        BLEPrinter.runCmd(data:PrinterUtil.alignCenter())
        let qrcode = PrinterUtil.decodeBitmap(qrcodeImg)
        if qrcode.count > 0 {
            BLEPrinter.runCmd(data: qrcode)
        }
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data: NSString(string: "多谢惠顾！扫一扫 查看订单").dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data: PrinterUtil.feedPaperCutPartial())
    }
    
    // 进行打印
    // dict: 要打印的对象，Dictionary
    // logo: logo图片文件名
    // qrcode: 二维码图片文件名
    func process(dict: Dictionary<String, AnyObject>, logo: String, qrcode: String) {
        let storeName: String = dict["name"] as! String
        let storeContent: String = dict["content"] as! String
        let servicePhone: String = dict["servicePhone"] as! String
        let orderNo: String = dict["orderNo"] as! String
        // 注：这里假设 deliveryTime 已经格式化成了 String
        let deliveryTime: String = dict["deliveryTime"] as! String
        let serialNumber: String = dict["serialNumber"] as! String
        let payType: Int = dict["payType"] as! Int
        let address = dict["address"]  as! String
        let phone = dict["phone"] as! String
        let remark = dict["remark"] as! String
        let contacts = dict["contacts"] as! String
        let details: [AnyObject] = dict["detail"] as! [AnyObject]
        let totalFee: Int = dict["totalFee"] as! Int
        let paied : Bool = (payType == 1) // 1: 网上支付，2: 现金支付
        
        // 打印logo
        initPrinter()
        // 确保是默认行高
        BLEPrinter.runCmd(data: PrinterUtil.printLineNormalHeight())
        // 确保不是粗体
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(0))
        BLEPrinter.runCmd(data:PrinterUtil.emphasizedOff())
        
        printMazingHeader(logo)
        printStoreName(storeName, brief: storeContent, servicePhone: servicePhone)
        
        // 这两个不加粗
        printOrderNumber(orderNo)
        printTime("预计送达时间：", time: deliveryTime)
        
        // 以下内容行距70
        BLEPrinter.runCmd(data: PrinterUtil.printLineHeight(70))
        //BLEPrinter.runCmd(data:PrinterUtil.emphasizedOn())
        
        // 打印订单序列号，左对齐
        printSerialNumber(serialNumber, paied: paied)
        printAddress(address)
        printContact(phone, name: contacts, remark: remark)
        
        // 恢复正常行距
        BLEPrinter.runCmd(data: PrinterUtil.printLineNormalHeight())
        
        printOrderDetails(details)
        
        
        // 打印合计，右对齐，换行
        printTotalPrice("合计：", price: totalFee, paied: paied)
        
        // 正常字体
        //BLEPrinter.runCmd(data:PrinterUtil.emphasizedOff())
        
        // 打印二维码
        printMazingFooter(qrcode)
    }
    
    //
    // 打印厨房单，并切纸
    // serialNumber: 订单流水号，比如#99
    // time: 订单预计送达时间
    // name: 菜名
    // number: 数量
    // remark: 备注（特殊要求）
    //
    func printKitchenOrder(serialNumber: String, time: String, name: String, number: Int, remark: String) {
        // 计算要多少空格
        var spaceLen = getMaxTextLength() - ConvertUtil.getTextLength(serialNumber) - ConvertUtil.getTextLength(time) - ConvertUtil.getTextLength("预计送达：")
        var topTxt = ""
        if spaceLen > 0 {
            topTxt = serialNumber + makeSpace(spaceLen) + "预计送达：" + time
        }
        else {
            topTxt = serialNumber + " " + "预计送达：" + time
        }
        // 打印顶部
        // 确保是默认行高
        BLEPrinter.runCmd(data: PrinterUtil.printLineNormalHeight())
        // 确保不是粗体
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(0))
        BLEPrinter.runCmd(data:PrinterUtil.emphasizedOff())
        
        BLEPrinter.runCmd(data: PrinterUtil.alignLeft());
        BLEPrinter.runCmd(data: NSString(string: topTxt).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data: PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data: NSString(string: getHorizonLine()).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data: PrinterUtil.printLineFeed())

        // 打印菜名
        // 逻辑：(1) 菜名左对齐，数量右对齐，如果1行可以打印完，直接打印；
        //      (2) 如果要两行才可以打印完，在菜名和数量之间补全空格，让数量在第二行的最右边显示；
        let numTxt = "×" + String(number)
        // 注：所有字体放大两倍，包括空格，所以减去的文本长度要乘以 2，空格数要除以2
        // 注2：为了兼容不同的打印机，空格减少 1 个再除以2
        spaceLen = (getMaxTextLength() - ConvertUtil.getTextLength(name) * 2 - ConvertUtil.getTextLength(numTxt) * 2 - 1) / 2
        var dishTxt = ""
        
        if spaceLen > 0 {
            dishTxt = name + makeSpace(spaceLen) + numTxt
        }
        else {
            spaceLen = (getMaxTextLength() * 2 - ConvertUtil.getTextLength(name) * 2 - ConvertUtil.getTextLength(numTxt) * 2 - 1) / 2
            let spaceTxt = (spaceLen > 0 ? makeSpace(spaceLen) : "")
            
            dishTxt = name + spaceTxt + numTxt
        }

        BLEPrinter.runCmd(data: PrinterUtil.printLineHeight(70))
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(1))
        
        BLEPrinter.runCmd(data: NSString(string: dishTxt).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data: PrinterUtil.fontSizeSetBig(0))
        BLEPrinter.runCmd(data: PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data: PrinterUtil.printLineNormalHeight())
        BLEPrinter.runCmd(data: NSString(string: "全单备注：" + remark).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data: PrinterUtil.printLineFeed())
        BLEPrinter.runCmd(data:PrinterUtil.printAndFeedLines(UInt8( 1 )))
        BLEPrinter.runCmd(data: PrinterUtil.feedPaperCutPartial())

    }
}

//
// 80mm 宽度的打印机实现
//
class MazingPrinter80: MazingPrinter {
    
    // 打印地址
    // 1倍字体，不加粗
    override func printAddress(address: String) {
        BLEPrinter.runCmd(data: PrinterUtil.alignLeft())
        BLEPrinter.runCmd(data: NSString(string: address).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed())
    }
    
    //
    // 打印机每行支持的最大字符长度（最小字体，以英文字符衡量）
    // 子类应该 override
    override func getMaxTextLength() -> Int {
        return 40
    }
    
    // 返回头部的横线，两倍粗体，不同纸张不一样
    // 子类应该 override
    override func getHeaderHorizonLine() -> String {
        return "───────"
    }
    
    // 返回一个横线，默认字体，不同纸张不一样
    // 子类应该 override
    override func getHorizonLine() -> String {
        return "─────────────────────"
    }

}

//
// 58mm 宽度的打印机实现
//
class MazingPrinter58: MazingPrinter {
    
    // 打印地址
    // 1倍字体，不加粗
    override func printAddress(address: String) {
        BLEPrinter.runCmd(data: PrinterUtil.alignLeft())
        BLEPrinter.runCmd(data: NSString(string: address).dataUsingEncoding(encoding)!)
        BLEPrinter.runCmd(data:PrinterUtil.printLineFeed());
    }

    //
    // 打印机每行支持的最大字符长度（最小字体，以英文字符衡量）
    // 子类应该 override
    override func getMaxTextLength() -> Int {
        return 30
    }
    
    // 返回头部的横线，两倍粗体，不同纸张不一样
    // 子类应该 override
    override func getHeaderHorizonLine() -> String {
        return "─────"
    }
    
    // 返回一个横线，默认字体，不同纸张不一样
    // 子类应该 override
    override func getHorizonLine() -> String {
        return "────────────────"
    }

}

class BLEPrinter{
    
    class func runCmd(data d:[UInt8],tag t:Int = 0,withTimeout wt:Double = -1)->Void{
        let nsdatas = NSData(bytes:d, length: d.count)
        let sendData:BLESendData = BLESendData(CBCharacteristic: nil, cmdData: nsdatas)
        BLEManager.shareInstance().joinSendData(sendData)
    
    }
    
    class func runCmd(str s:String,tag t:Int = 0,withTimeout wt:Double = -1)->Void{
        
        let cfEnc = CFStringEncodings.GB_18030_2000
        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(cfEnc.rawValue))
        let nsdatas = s.dataUsingEncoding(enc, allowLossyConversion: true)!
        let sendData:BLESendData = BLESendData(CBCharacteristic: nil, cmdData: nsdatas)
               BLEManager.shareInstance().joinSendData(sendData)
       
    }
    
    class func runCmd(data d:NSData) {
        
        let sendData:BLESendData = BLESendData(CBCharacteristic: nil, cmdData: d)
        BLEManager.shareInstance().joinSendData(sendData)
    }
}

